"""交易执行引擎."""

from __future__ import annotations

import logging
from dataclasses import dataclass
from typing import List

import numpy as np
import pandas as pd

from .config import BacktestConfig

logger = logging.getLogger(__name__)


@dataclass
class Trade:
    """交易记录."""

    date: pd.Timestamp
    action: str  # 'buy' or 'sell'
    price: float
    quantity: float
    commission: float
    slippage: float
    total_cost: float


@dataclass
class Position:
    """持仓记录."""

    date: pd.Timestamp
    quantity: float
    cost_price: float
    current_price: float
    unrealized_pnl: float
    unrealized_pnl_pct: float


class TradeExecutor:
    """交易执行引擎."""

    def __init__(self, config: BacktestConfig) -> None:
        self.config = config
        self.trades: List[Trade] = []
        self.positions: List[Position] = []

    def execute_trade(
        self,
        date: pd.Timestamp,
        action: str,
        signal_price: float,
        current_position: float,
        capital: float,
        entry_price: float | None = None,
        current_price: float | None = None,
    ) -> tuple[float, float, Trade | None]:
        """执行交易.

        Parameters
        ----------
        date:
            交易日期
        action:
            交易动作：'buy' 或 'sell'
        signal_price:
            信号价格（收盘价）
        current_position:
            当前仓位（0=空仓，1=满仓）
        capital:
            买入时：当前资金（现金）
            卖出时：当前持仓价值
        entry_price:
            入场价格（卖出时需要，用于计算持仓数量）
        current_price:
            当前价格（卖出时需要，用于计算持仓数量），如果为 None，则使用 signal_price

        Returns
        -------
        (new_position, new_capital, trade_record)
        new_position: 新仓位（0或1）
        new_capital: 新资金
        trade_record: 交易记录，如果未执行交易则为 None
        """
        # 计算成交价格（考虑滑点）
        if action == "buy":
            execution_price = signal_price * (1 + self.config.slippage_rate)
            if current_position == 1:
                # 已满仓，不执行
                return current_position, capital, None

            # 计算可买入数量（满仓）
            # capital 是现金
            quantity = capital / execution_price
            commission = capital * self.config.commission_rate
            slippage_cost = capital * self.config.slippage_rate
            total_cost = capital + commission + slippage_cost

            new_position = 1.0
            new_capital = 0.0

        elif action == "sell":
            execution_price = signal_price * (1 - self.config.slippage_rate)
            if current_position == 0:
                # 已空仓，不执行
                return current_position, capital, None

            # 卖出全部持仓
            # capital 是持仓价值（基于当前价格）
            # 如果没有提供 current_price，使用 signal_price 作为当前价格
            if current_price is None or current_price <= 0:
                current_price = signal_price
            
            # 持仓数量 = 持仓价值 / 当前价格
            quantity = capital / current_price
            
            # 计算卖出收益（考虑滑点）
            # 卖出收益 = 持仓数量 * 卖出价格 * (1 - 滑点率) - 手续费
            # 由于 capital 是当前持仓价值 = 持仓数量 * current_price
            # 所以：卖出收益 = capital * (execution_price / current_price) * (1 - 滑点率) - 手续费
            # 但 execution_price 已经包含了滑点，所以：
            proceeds = capital * (execution_price / current_price)
            commission = proceeds * self.config.commission_rate
            new_capital = proceeds - commission
            total_cost = capital - new_capital  # 持仓价值 - 实际收益（包括滑点和手续费）
            new_position = 0.0

        else:
            # 中性，不交易
            return current_position, capital, None

        trade = Trade(
            date=date,
            action=action,
            price=execution_price,
            quantity=quantity,
            commission=commission,
            slippage=slippage_cost if action == "buy" else capital - proceeds,
            total_cost=total_cost,
        )
        self.trades.append(trade)

        return new_position, new_capital, trade

    def check_stop_loss_take_profit(
        self,
        date: pd.Timestamp,
        high: float,
        low: float,
        current_position: float,
        entry_price: float,
        capital: float,
        current_price: float | None = None,
    ) -> tuple[float, float, Trade | None]:
        """检查止损/止盈.

        Parameters
        ----------
        date:
            交易日期
        high:
            当日最高价
        low:
            当日最低价
        current_position:
            当前仓位（0=空仓，1=满仓）
        entry_price:
            入场价格
        capital:
            当前持仓价值（基于当前价格）
        current_price:
            当前价格（用于计算持仓数量），如果为 None，则使用 entry_price 作为近似值

        Returns
        -------
        (new_position, new_capital, trade_record)
        如果触发止损/止盈，返回交易记录；否则返回 None
        """
        if current_position == 0:
            return current_position, capital, None

        # 如果没有提供当前价格，使用 entry_price 作为近似值
        if current_price is None or current_price <= 0:
            current_price = entry_price

        # 检查止损（优先）
        stop_loss_price = entry_price * (1 - self.config.stop_loss_pct)
        if low <= stop_loss_price:
            # 触发止损
            execution_price = stop_loss_price * (1 - self.config.slippage_rate)  # 考虑滑点
            # capital 是基于 current_price 的持仓价值
            # 持仓数量 = capital / current_price
            # 止损时的持仓价值 = 持仓数量 * stop_loss_price = capital * (stop_loss_price / current_price)
            stop_loss_value = capital * (stop_loss_price / current_price)
            proceeds = stop_loss_value * (1 - self.config.slippage_rate)
            commission = proceeds * self.config.commission_rate
            new_capital = proceeds - commission

            # 计算持仓数量（用于记录）
            quantity = capital / current_price

            trade = Trade(
                date=date,
                action="sell",
                price=execution_price,
                quantity=quantity,
                commission=commission,
                slippage=stop_loss_value - proceeds,  # 滑点损失
                total_cost=capital - new_capital,  # 持仓价值 - 实际收益
            )
            self.trades.append(trade)
            return 0.0, new_capital, trade

        # 检查止盈
        take_profit_price = entry_price * (1 + self.config.take_profit_pct)
        if high >= take_profit_price:
            # 触发止盈
            execution_price = take_profit_price * (1 - self.config.slippage_rate)  # 考虑滑点
            # capital 是基于 current_price 的持仓价值
            # 持仓数量 = capital / current_price
            # 止盈时的持仓价值 = 持仓数量 * take_profit_price = capital * (take_profit_price / current_price)
            take_profit_value = capital * (take_profit_price / current_price)
            proceeds = take_profit_value * (1 - self.config.slippage_rate)
            commission = proceeds * self.config.commission_rate
            new_capital = proceeds - commission

            # 计算持仓数量（用于记录）
            quantity = capital / current_price

            trade = Trade(
                date=date,
                action="sell",
                price=execution_price,
                quantity=quantity,
                commission=commission,
                slippage=take_profit_value - proceeds,  # 滑点损失
                total_cost=capital - new_capital,  # 持仓价值 - 实际收益
            )
            self.trades.append(trade)
            return 0.0, new_capital, trade

        return current_position, capital, None

    def update_position(
        self,
        date: pd.Timestamp,
        current_price: float,
        position: float,
        entry_price: float,
        capital: float,
    ) -> Position:
        """更新持仓记录.

        Parameters
        ----------
        date:
            日期
        current_price:
            当前价格
        position:
            仓位（0或1）
        entry_price:
            入场价格
        capital:
            当前资金

        Returns
        -------
        持仓记录
        """
        if position == 0:
            unrealized_pnl = 0.0
            unrealized_pnl_pct = 0.0
        else:
            unrealized_pnl = capital * (current_price / entry_price - 1)
            unrealized_pnl_pct = (current_price / entry_price - 1) * 100

        pos = Position(
            date=date,
            quantity=position,
            cost_price=entry_price,
            current_price=current_price,
            unrealized_pnl=unrealized_pnl,
            unrealized_pnl_pct=unrealized_pnl_pct,
        )
        self.positions.append(pos)
        return pos

